home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / Mail / lex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-02-09  |  12.6 KB  |  669 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. static char sccsid[] = "@(#)lex.c    5.18 (Berkeley) 2/13/89";
  20. #endif /* not lint */
  21.  
  22. #include "rcv.h"
  23. #include <sys/stat.h>
  24. #include <errno.h>
  25.  
  26. /*
  27.  * Mail -- a mail program
  28.  *
  29.  * Lexical processing of commands.
  30.  */
  31.  
  32. char    *prompt = "& ";
  33.  
  34. /*
  35.  * Set up editing on the given file name.
  36.  * If the first character of name is %, we are considered to be
  37.  * editing the file, otherwise we are reading our mail which has
  38.  * signficance for mbox and so forth.
  39.  */
  40. setfile(name)
  41.     char *name;
  42. {
  43.     FILE *ibuf;
  44.     int i;
  45.     struct stat stb;
  46.     char isedit = *name != '%';
  47.     char *who = name[1] ? name + 1 : myname;
  48.     static int shudclob;
  49.     extern char tempMesg[];
  50.     extern int errno;
  51.  
  52.     if ((name = expand(name)) == NOSTR)
  53.         return -1;
  54.  
  55.     if ((ibuf = fopen(name, "r")) == NULL) {
  56.         if (!isedit && errno == ENOENT)
  57.             goto nomail;
  58.         perror(name);
  59.         return(-1);
  60.     }
  61.  
  62.     if (fstat(fileno(ibuf), &stb) < 0) {
  63.         perror("fstat");
  64.         fclose(ibuf);
  65.         return (-1);
  66.     }
  67.  
  68.     switch (stb.st_mode & S_IFMT) {
  69.     case S_IFDIR:
  70.         fclose(ibuf);
  71.         errno = EISDIR;
  72.         perror(name);
  73.         return (-1);
  74.  
  75.     case S_IFREG:
  76.         break;
  77.  
  78.     default:
  79.         fclose(ibuf);
  80.         errno = EINVAL;
  81.         perror(name);
  82.         return (-1);
  83.     }
  84.  
  85.     /*
  86.      * Looks like all will be well.  We must now relinquish our
  87.      * hold on the current set of stuff.  Must hold signals
  88.      * while we are reading the new file, else we will ruin
  89.      * the message[] data structure.
  90.      */
  91.  
  92.     holdsigs();
  93.     if (shudclob)
  94.         quit();
  95.  
  96.     /*
  97.      * Copy the messages into /tmp
  98.      * and set pointers.
  99.      */
  100.  
  101.     readonly = 0;
  102.     if ((i = open(name, 1)) < 0)
  103.         readonly++;
  104.     else
  105.         close(i);
  106.     if (shudclob) {
  107.         fclose(itf);
  108.         fclose(otf);
  109.     }
  110.     shudclob = 1;
  111.     edit = isedit;
  112.     strcpy(prevfile, mailname);
  113.     if (name != mailname)
  114.         strcpy(mailname, name);
  115.     mailsize = fsize(ibuf);
  116.     if ((otf = fopen(tempMesg, "w")) == NULL) {
  117.         perror(tempMesg);
  118.         exit(1);
  119.     }
  120.     if ((itf = fopen(tempMesg, "r")) == NULL) {
  121.         perror(tempMesg);
  122.         exit(1);
  123.     }
  124.     remove(tempMesg);
  125.     setptr(ibuf);
  126.     setmsize(msgCount);
  127.     fclose(ibuf);
  128.     relsesigs();
  129.     sawcom = 0;
  130.     if (!edit && msgCount == 0) {
  131. nomail:
  132.         fprintf(stderr, "No mail for %s\n", who);
  133.         return -1;
  134.     }
  135.     return(0);
  136. }
  137.  
  138. int    *msgvec;
  139. int    reset_on_stop;            /* do a reset() if stopped */
  140.  
  141. /*
  142.  * Interpret user commands one by one.  If standard input is not a tty,
  143.  * print no prompt.
  144.  */
  145. commands()
  146. {
  147.     int eofloop = 0;
  148.     register int n;
  149.     char linebuf[LINESIZE];
  150.     int intr(), stop(), hangup(), contin();
  151.  
  152.     if (!sourcing) {
  153.         if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  154.             signal(SIGINT, intr);
  155.         if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
  156.             signal(SIGHUP, hangup);
  157.         signal(SIGTSTP, stop);
  158.         signal(SIGTTOU, stop);
  159.         signal(SIGTTIN, stop);
  160.     }
  161.     setexit();
  162.     for (;;) {
  163.         /*
  164.          * Print the prompt, if needed.  Clear out
  165.          * string space, and flush the output.
  166.          */
  167.         if (!sourcing && value("interactive") != NOSTR) {
  168.             reset_on_stop = 1;
  169.             printf(prompt);
  170.         }
  171.         fflush(stdout);
  172.         sreset();
  173.         /*
  174.          * Read a line of commands from the current input
  175.          * and handle end of file specially.
  176.          */
  177.         n = 0;
  178.         for (;;) {
  179.             if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
  180.                 if (n == 0)
  181.                     n = -1;
  182.                 break;
  183.             }
  184.             if ((n = strlen(linebuf)) == 0)
  185.                 break;
  186.             n--;
  187.             if (linebuf[n] != '\\')
  188.                 break;
  189.             linebuf[n++] = ' ';
  190.         }
  191.         reset_on_stop = 0;
  192.         if (n < 0) {
  193.                 /* eof */
  194.             if (loading)
  195.                 break;
  196.             if (sourcing) {
  197.                 unstack();
  198.                 continue;
  199.             }
  200.             if (value("interactive") != NOSTR &&
  201.                 value("ignoreeof") != NOSTR &&
  202.                 ++eofloop < 25) {
  203.                 printf("Use \"quit\" to quit.\n");
  204.                 continue;
  205.             }
  206.             break;
  207.         }
  208.         eofloop = 0;
  209.         if (execute(linebuf, 0))
  210.             break;
  211.     }
  212. }
  213.  
  214. /*
  215.  * Execute a single command.
  216.  * Command functions return 0 for success, 1 for error, and -1
  217.  * for abort.  A 1 or -1 aborts a load or source.  A -1 aborts
  218.  * the interactive command loop.
  219.  * Contxt is non-zero if called while composing mail.
  220.  */
  221. execute(linebuf, contxt)
  222.     char linebuf[];
  223. {
  224.     char word[LINESIZE];
  225.     char *arglist[MAXARGC];
  226.     struct cmd *com;
  227.     register char *cp, *cp2;
  228.     register int c;
  229.     int muvec[2];
  230.     int e = 1;
  231.  
  232.     /*
  233.      * Strip the white space away from the beginning
  234.      * of the command, then scan out a word, which
  235.      * consists of anything except digits and white space.
  236.      *
  237.      * Handle ! escapes differently to get the correct
  238.      * lexical conventions.
  239.      */
  240.  
  241.     for (cp = linebuf; isspace(*cp); cp++)
  242.         ;
  243.     if (*cp == '!') {
  244.         if (sourcing) {
  245.             printf("Can't \"!\" while sourcing\n");
  246.             goto out;
  247.         }
  248.         shell(cp+1);
  249.         return(0);
  250.     }
  251.     cp2 = word;
  252.     while (*cp && index(" \t0123456789$^.:/-+*'\"", *cp) == NOSTR)
  253.         *cp2++ = *cp++;
  254.     *cp2 = '\0';
  255.  
  256.     /*
  257.      * Look up the command; if not found, bitch.
  258.      * Normally, a blank command would map to the
  259.      * first command in the table; while sourcing,
  260.      * however, we ignore blank lines to eliminate
  261.      * confusion.
  262.      */
  263.  
  264.     if (sourcing && *word == '\0')
  265.         return(0);
  266.     com = lex(word);
  267.     if (com == NONE) {
  268.         printf("Unknown command: \"%s\"\n", word);
  269.         goto out;
  270.     }
  271.  
  272.     /*
  273.      * See if we should execute the command -- if a conditional
  274.      * we always execute it, otherwise, check the state of cond.
  275.      */
  276.  
  277.     if ((com->c_argtype & F) == 0)
  278.         if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
  279.             return(0);
  280.  
  281.     /*
  282.      * Process the arguments to the command, depending
  283.      * on the type he expects.  Default to an error.
  284.      * If we are sourcing an interactive command, it's
  285.      * an error.
  286.      */
  287.  
  288.     if (!rcvmode && (com->c_argtype & M) == 0) {
  289.         printf("May not execute \"%s\" while sending\n",
  290.             com->c_name);
  291.         goto out;
  292.     }
  293.     if (sourcing && com->c_argtype & I) {
  294.         printf("May not execute \"%s\" while sourcing\n",
  295.             com->c_name);
  296.         goto out;
  297.     }
  298.     if (readonly && com->c_argtype & W) {
  299.         printf("May not execute \"%s\" -- message file is read only\n",
  300.            com->c_name);
  301.         goto out;
  302.     }
  303.     if (contxt && com->c_argtype & R) {
  304.         printf("Cannot recursively invoke \"%s\"\n", com->c_name);
  305.         goto out;
  306.     }
  307.     switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
  308.     case MSGLIST:
  309.         /*
  310.          * A message list defaulting to nearest forward
  311.          * legal message.
  312.          */
  313.         if (msgvec == 0) {
  314.             printf("Illegal use of \"message list\"\n");
  315.             break;
  316.         }
  317.         if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
  318.             break;
  319.         if (c  == 0) {
  320.             *msgvec = first(com->c_msgflag,
  321.                 com->c_msgmask);
  322.             msgvec[1] = NULL;
  323.         }
  324.         if (*msgvec == NULL) {
  325.             printf("No applicable messages\n");
  326.             break;
  327.         }
  328.         e = (*com->c_func)(msgvec);
  329.         break;
  330.  
  331.     case NDMLIST:
  332.         /*
  333.          * A message list with no defaults, but no error
  334.          * if none exist.
  335.          */
  336.         if (msgvec == 0) {
  337.             printf("Illegal use of \"message list\"\n");
  338.             break;
  339.         }
  340.         if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
  341.             break;
  342.         e = (*com->c_func)(msgvec);
  343.         break;
  344.  
  345.     case STRLIST:
  346.         /*
  347.          * Just the straight string, with
  348.          * leading blanks removed.
  349.          */
  350.         while (isspace(*cp))
  351.             cp++;
  352.         e = (*com->c_func)(cp);
  353.         break;
  354.  
  355.     case RAWLIST:
  356.         /*
  357.          * A vector of strings, in shell style.
  358.          */
  359.         if ((c = getrawlist(cp, arglist,
  360.                 sizeof arglist / sizeof *arglist)) < 0)
  361.             break;
  362.         if (c < com->c_minargs) {
  363.             printf("%s requires at least %d arg(s)\n",
  364.                 com->c_name, com->c_minargs);
  365.             break;
  366.         }
  367.         if (c > com->c_maxargs) {
  368.             printf("%s takes no more than %d arg(s)\n",
  369.                 com->c_name, com->c_maxargs);
  370.             break;
  371.         }
  372.         e = (*com->c_func)(arglist);
  373.         break;
  374.  
  375.     case NOLIST:
  376.         /*
  377.          * Just the constant zero, for exiting,
  378.          * eg.
  379.          */
  380.         e = (*com->c_func)(0);
  381.         break;
  382.  
  383.     default:
  384.         panic("Unknown argtype");
  385.     }
  386.  
  387. out:
  388.     /*
  389.      * Exit the current source file on
  390.      * error.
  391.      */
  392.     if (e) {
  393.         if (e < 0)
  394.             return 1;
  395.         if (loading)
  396.             return 1;
  397.         if (sourcing)
  398.             unstack();
  399.         return 0;
  400.     }
  401.     if (value("autoprint") != NOSTR && com->c_argtype & P)
  402.         if ((dot->m_flag & MDELETED) == 0) {
  403.             muvec[0] = dot - &message[0] + 1;
  404.             muvec[1] = 0;
  405.             type(muvec);
  406.         }
  407.     if (!sourcing && (com->c_argtype & T) == 0)
  408.         sawcom = 1;
  409.     return(0);
  410. }
  411.  
  412. /*
  413.  * Set the size of the message vector used to construct argument
  414.  * lists to message list functions.
  415.  */
  416.  
  417. setmsize(sz)
  418. {
  419.  
  420.     if (msgvec != 0)
  421.         cfree((char *) msgvec);
  422.     msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
  423. }
  424.  
  425. /*
  426.  * Find the correct command in the command table corresponding
  427.  * to the passed command "word"
  428.  */
  429.  
  430. struct cmd *
  431. lex(word)
  432.     char word[];
  433. {
  434.     register struct cmd *cp;
  435.     extern struct cmd cmdtab[];
  436.  
  437.     for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
  438.         if (isprefix(word, cp->c_name))
  439.             return(cp);
  440.     return(NONE);
  441. }
  442.  
  443. /*
  444.  * Determine if as1 is a valid prefix of as2.
  445.  * Return true if yep.
  446.  */
  447.  
  448. isprefix(as1, as2)
  449.     char *as1, *as2;
  450. {
  451.     register char *s1, *s2;
  452.  
  453.     s1 = as1;
  454.     s2 = as2;
  455.     while (*s1++ == *s2)
  456.         if (*s2++ == '\0')
  457.             return(1);
  458.     return(*--s1 == '\0');
  459. }
  460.  
  461. /*
  462.  * The following gets called on receipt of an interrupt.  This is
  463.  * to abort printout of a command, mainly.
  464.  * Dispatching here when command() is inactive crashes rcv.
  465.  * Close all open files except 0, 1, 2, and the temporary.
  466.  * Also, unstack all source files.
  467.  */
  468.  
  469. int    inithdr;            /* am printing startup headers */
  470.  
  471. #ifdef _NFILE
  472. static
  473. _fwalk(function)
  474.     register int (*function)();
  475. {
  476.     register FILE *iop;
  477.  
  478.     for (iop = _iob; iop < _iob + _NFILE; iop++)
  479.         (*function)(iop);
  480. }
  481. #else
  482. static
  483. _fwalk(function)
  484.     register int (*function)();
  485. {
  486. }
  487. #endif
  488.  
  489. static
  490. xclose(iop)
  491.     register FILE *iop;
  492. {
  493.     if (iop == stdin || iop == stdout ||
  494.         iop == stderr || iop == itf || iop == otf)
  495.         return;
  496.  
  497.     if (iop != pipef)
  498.         fclose(iop);
  499.     else {
  500.         Pclose(pipef);
  501.         pipef = NULL;
  502.     }
  503. }
  504.  
  505. /*ARGSUSED*/
  506. intr(s)
  507. {
  508.  
  509.     noreset = 0;
  510.     if (!inithdr)
  511.         sawcom++;
  512.     inithdr = 0;
  513.     while (sourcing)
  514.         unstack();
  515.  
  516.     /*
  517.      * Walk through all the open FILEs, applying xclose() to them
  518.      */
  519.     _fwalk(xclose);
  520.  
  521.     if (image >= 0) {
  522.         close(image);
  523.         image = -1;
  524.     }
  525.     fprintf(stderr, "Interrupt\n");
  526.     reset(0);
  527. }
  528.  
  529. /*
  530.  * When we wake up after ^Z, reprint the prompt.
  531.  */
  532. stop(s)
  533. {
  534.     int (*old_action)() = signal(s, SIG_DFL);
  535.  
  536.     sigsetmask(sigblock(0) & ~sigmask(s));
  537.     kill(0, s);
  538.     sigblock(sigmask(s));
  539.     signal(s, old_action);
  540.     if (reset_on_stop) {
  541.         reset_on_stop = 0;
  542.         reset(0);
  543.     }
  544. }
  545.  
  546. /*
  547.  * Branch here on hangup signal and simulate "exit".
  548.  */
  549. /*ARGSUSED*/
  550. hangup(s)
  551. {
  552.  
  553.     /* nothing to do? */
  554.     exit(1);
  555. }
  556.  
  557. /*
  558.  * Announce the presence of the current Mail version,
  559.  * give the message count, and print a header listing.
  560.  */
  561.  
  562. announce()
  563. {
  564.     int vec[2], mdot;
  565.  
  566.     mdot = newfileinfo();
  567.     vec[0] = mdot;
  568.     vec[1] = 0;
  569.     dot = &message[mdot - 1];
  570.     if (msgCount > 0 && value("noheader") == NOSTR) {
  571.         inithdr++;
  572.         headers(vec);
  573.         inithdr = 0;
  574.     }
  575. }
  576.  
  577. /*
  578.  * Announce information about the file we are editing.
  579.  * Return a likely place to set dot.
  580.  */
  581. newfileinfo()
  582. {
  583.     register struct message *mp;
  584.     register int u, n, mdot, d, s;
  585.     char fname[BUFSIZ], zname[BUFSIZ], *ename;
  586.  
  587.     for (mp = &message[0]; mp < &message[msgCount]; mp++)
  588.         if (mp->m_flag & MNEW)
  589.             break;
  590.     if (mp >= &message[msgCount])
  591.         for (mp = &message[0]; mp < &message[msgCount]; mp++)
  592.             if ((mp->m_flag & MREAD) == 0)
  593.                 break;
  594.     if (mp < &message[msgCount])
  595.         mdot = mp - &message[0] + 1;
  596.     else
  597.         mdot = 1;
  598.     s = d = 0;
  599.     for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
  600.         if (mp->m_flag & MNEW)
  601.             n++;
  602.         if ((mp->m_flag & MREAD) == 0)
  603.             u++;
  604.         if (mp->m_flag & MDELETED)
  605.             d++;
  606.         if (mp->m_flag & MSAVED)
  607.             s++;
  608.     }
  609.     ename = mailname;
  610.     if (getfold(fname) >= 0) {
  611.         strcat(fname, "/");
  612.         if (strncmp(fname, mailname, strlen(fname)) == 0) {
  613.             sprintf(zname, "+%s", mailname + strlen(fname));
  614.             ename = zname;
  615.         }
  616.     }
  617.     printf("\"%s\": ", ename);
  618.     if (msgCount == 1)
  619.         printf("1 message");
  620.     else
  621.         printf("%d messages", msgCount);
  622.     if (n > 0)
  623.         printf(" %d new", n);
  624.     if (u-n > 0)
  625.         printf(" %d unread", u);
  626.     if (d > 0)
  627.         printf(" %d deleted", d);
  628.     if (s > 0)
  629.         printf(" %d saved", s);
  630.     if (readonly)
  631.         printf(" [Read only]");
  632.     printf("\n");
  633.     return(mdot);
  634. }
  635.  
  636. /*
  637.  * Print the current version number.
  638.  */
  639.  
  640. /*ARGSUSED*/
  641. pversion(e)
  642. {
  643.     extern char *version;
  644.  
  645.     printf("Version %s\n", version);
  646.     return(0);
  647. }
  648.  
  649. /*
  650.  * Load a file of user definitions.
  651.  */
  652. load(name)
  653.     char *name;
  654. {
  655.     register FILE *in, *oldin;
  656.  
  657.     if ((in = fopen(name, "r")) == NULL)
  658.         return;
  659.     oldin = input;
  660.     input = in;
  661.     loading = 1;
  662.     sourcing = 1;
  663.     commands();
  664.     loading = 0;
  665.     sourcing = 0;
  666.     input = oldin;
  667.     fclose(in);
  668. }
  669.